﻿using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using HarmonyLib;

namespace BepInEx.Unix
{
    internal static class ConsoleWriter
    {
        private static Func<Stream, Encoding, bool, StreamWriter> cStreamWriterConstructor;

        private static Func<Stream, Encoding, bool, StreamWriter> CStreamWriterConstructor
        {
            get
            {
                if (cStreamWriterConstructor != null)
                    return cStreamWriterConstructor;

                var cStreamWriter = AccessTools.TypeByName("System.IO.CStreamWriter");

                Func<Stream, Encoding, bool, StreamWriter> GetCtor(int[] perm)
                {
                    var parameters = new[] { typeof(Stream), typeof(Encoding), typeof(bool) };
                    var ctor = AccessTools.Constructor(cStreamWriter, perm.Select(i => parameters[i]).ToArray());
                    if (ctor != null)
                        return (stream, encoding, l) =>
                        {
                            var vals = new object[] { stream, encoding, l };
                            return (StreamWriter) ctor.Invoke(perm.Select(i => vals[i]).ToArray());
                        };
                    return null;
                }

                var ctorParams = new[]
                {
                    new[] { 0, 1, 2 }, // Unity 5.x and up
                    new[] { 0, 1 }     // Unity 4.7 and older
                };

                cStreamWriterConstructor = ctorParams.Select(GetCtor).FirstOrDefault(f => f != null);
                if (cStreamWriterConstructor == null)
                    throw new AmbiguousMatchException("Failed to find suitable constructor for CStreamWriter");
                return cStreamWriterConstructor;
            }
        }

        public static TextWriter CreateConsoleStreamWriter(Stream stream, Encoding encoding, bool leaveOpen)
        {
            var writer = CStreamWriterConstructor(stream, encoding, leaveOpen);
            writer.AutoFlush = true;
            return writer;
        }
    }
}
